/*
 * TagFlowLayout.java
 * business
 *
 * Created by ChenTao on 2017/2/21.
 *
 * Copyright (c) 2017年 yidiandao. All rights reserved.
 */

package com.yidiandao.business.ui.widget.view;

import android.content.Context;
import android.content.res.TypedArray;
import android.text.Editable;
import android.text.InputFilter;
import android.text.TextUtils;
import android.text.TextWatcher;
import android.util.AttributeSet;
import android.util.TypedValue;
import android.view.KeyEvent;
import android.view.View;
import android.view.ViewGroup;
import android.widget.EditText;

import com.yidiandao.business.R;
import com.yidiandao.business.data.bean.ITag;
import com.yidiandao.business.data.bean.Tag;
import com.yidiandao.business.utils.UIUtils;

import java.util.LinkedHashMap;
import java.util.List;
import java.util.Map;

/**
 * 标签的流布局, 根据标签的宽高自动换行.
 */
public class TagFlowLayout extends ViewGroup {

    /**
     * 默认水平间隔, 10dp.
     */
    private static final float DEFAULT_HORIZONTAL_SPACING = 10;

    /**
     * 默认垂直间隔, 10dp.
     */
    private static final float DEFAULT_VERTICAL_SPACING = 10;

    /**
     * 中文逗号.
     */
    private static final char PERIOD_EN = ',';

    /**
     * 英文逗号.
     */
    private static final char PERIOD_CN = '，';

    /**
     * 水平间隔.
     */
    private int mHorizontalSpacing;

    /**
     * 垂直间隔.
     */
    private int mVerticalSpacing;

    /**
     * 标签状态.
     */
    private int mState;

    /**
     * 标签最大的数量.
     */
    private int mMaxTagNum;

    /**
     * 是否限制标签数量.
     */
    private boolean mIsLimitTagNum;

    private TagListener mTagListener;

    private LinkedHashMap<String, ITag> mMapTagsData;

    private ITag mSelectedTag;

    private TagClickHelperListener mClickHelperListener;

    public TagFlowLayout(Context context) {
        this(context, null);
    }

    public TagFlowLayout(Context context, AttributeSet attrs) {
        this(context, attrs, 0);
    }

    public TagFlowLayout(Context context, AttributeSet attrs, int defStyleAttr) {
        super(context, attrs, defStyleAttr);
        initAttrs(context, attrs);
        initVariables();
    }

    @Override
    protected void onMeasure(int widthMeasureSpec, int heightMeasureSpec) {

        // 通过计算 对child进行测量后，设置自己的宽度与高度
        int widthSize = MeasureSpec.getSize(widthMeasureSpec);

        int paddingLeft = getPaddingLeft();
        int paddingTop = getPaddingTop();
        int paddingRight = getPaddingRight();
        int paddingBottom = getPaddingBottom();

        int childLeft = paddingLeft;
        int childTop = paddingTop;

        int lineHeight = 0;
        View childView;
        for (int i = 0, childCount = getChildCount(); i < childCount; i++) {
            childView = getChildAt(i);
            // 测量Child，获取child的宽高信息
            measureChild(childView, widthMeasureSpec, heightMeasureSpec);
            // 测量之后可以获取到Child的宽度与高度
            int childWidth = childView.getMeasuredWidth();
            int childHeight = childView.getMeasuredHeight();

            lineHeight = Math.max(childHeight, lineHeight);

            // 注意:在测量时,每次的代码都指定的是下一个,相当于layout下一个
            if (childLeft + childWidth + paddingRight > widthSize) {
                // 换行,需要加上竖直方向间距,左边的距离需要加上paddingLeft,以及horizontalSpacing
                childLeft = paddingLeft + childWidth + mHorizontalSpacing;
                childTop += lineHeight + mVerticalSpacing;
                lineHeight = childHeight;
            } else {
                // 未换行,需要加上水平方向间距
                childLeft += childWidth + mHorizontalSpacing;
            }
        }

        // 高度要包含最后一行的高度
        int heightSize = childTop + lineHeight + paddingBottom;

        // 测量自己的宽度与高度
        setMeasuredDimension(widthSize, heightSize);
    }

    @Override
    protected void onLayout(boolean changed, int l, int t, int r, int b) {
        int widthSize = r - l;

        int paddingLeft = getPaddingLeft();
        int paddingTop = getPaddingTop();
        int paddingRight = getPaddingRight();

        int childLeft = paddingLeft;
        int childTop = paddingTop;

        int lineHeight = 0;
        View childView;
        for (int i = 0, childCount = getChildCount(); i < childCount; i++) {
            childView = getChildAt(i);

            if (childView.getVisibility() == View.GONE) {
                continue;
            }

            int childWidth = childView.getMeasuredWidth();
            int childHeight = childView.getMeasuredHeight();

            lineHeight = Math.max(childHeight, lineHeight);

            if (childLeft + childWidth + paddingRight > widthSize) {
                // 换行
                childLeft = paddingLeft;
                // 距离右边距离要加上右间距
                childTop += lineHeight + mVerticalSpacing;
                lineHeight = childHeight;
            }

            // 对child进行layout
            childView.layout(childLeft, childTop, childLeft + childWidth, childTop + childHeight);

            // 距离左边距离要加上间距
            childLeft += childWidth + mHorizontalSpacing;
        }
    }

    public void setTagListener(TagListener tagListener) {
        mTagListener = tagListener;
    }

    public void setTagState(@TagView.State int state) {
        mState = state;
        if (mState == TagView.INPUT) {
            EditText editText = genEditText();
            addView(editText);
        }
    }

    public void addTagView(TagView tagView) {
        tagView.setState(mState);
        if (mState == TagView.INPUT) {
            int childCount = getChildCount();
            if (childCount > 0) {
                // 移除EditText
                removeViewAt(childCount - 1);
            }
            // 将当前标签添加进TagFlowLayout中
            addView(tagView);
            EditText editText = genEditText();
            editText.requestFocus();
            // 生成一个新的EditText加入TagFlowLayout末尾
            addView(editText);
        } else {
            addView(tagView);
        }
    }

    /**
     * 添加标签数据, 生成标签.
     *
     * @param tagData  标签数据
     * @param isFilter 是否过滤重复标签, true - 过滤, false - 不过滤
     */
    public void addTagData(final ITag tagData, boolean isFilter) {
        int tagSize = mMapTagsData.size();
        if (mIsLimitTagNum && tagSize >= mMaxTagNum) {
            // 标签数量越界
            UIUtils.showToast(UIUtils.getString(R.string.search_max_tag_limit, mMaxTagNum));
            return;
        }
        if (!isFilter || !mMapTagsData.containsKey(tagData.getName())) {
            TagView tagView = new TagView(getContext());
            tagView.setState(mState);
            tagView.setTagData(tagData);
            tagView.setSelected(tagData.isSelected());
            tagView.setEnabled(tagData.isEnable());

            final TagView finalTagViewView = tagView;
            tagView.setOnClickListener(new OnClickListener() {
                @Override
                public void onClick(View v) {
                    if (mState == TagView.INPUT) {
                        deleteTag(finalTagViewView);
                    } else {

                        if (mClickHelperListener == null) {
                            selectTag(tagData, finalTagViewView);
                        } else if (mClickHelperListener.onTagClick(finalTagViewView)) {
                            selectTag(tagData, finalTagViewView);
                        }

                        if (mTagListener != null) {
                            mTagListener.onTagClick(finalTagViewView);
                        }
                    }
                }
            });
            mMapTagsData.put(tagData.getName(), tagData);
            addTagView(tagView);
        }
    }

    /**
     * 选中指定的标签
     */
    private void selectTag(ITag tagData, TagView tagView) {
        clearAllSelected();
        // 选中指定的标签
        tagView.setSelected(true);
        tagData.setSelected(true);
        mSelectedTag = tagData;
    }

    /**
     * 获取当前选择的标签
     */
    public ITag getSelectedTag() {
        return mSelectedTag;
    }

    public void clearAllSelected() {
        for (int i = 0; i < getChildCount(); i++) {
            if (getChildAt(i) instanceof TagView) {
                TagView tagView = (TagView) getChildAt(i);
                tagView.setSelected(false);
                tagView.getTagData().setSelected(false);
            }
        }
    }

    /**
     * 设置标签数据列表, 生成对应的标签.
     *
     * @param listTagsData 标签数据列表
     */
    public void setTagsData(List<? extends ITag> listTagsData) {
        mMapTagsData.clear();
        removeAllViews();
        if (mState == TagView.INPUT) {
            addView(genEditText());
        }
        int tagSize = listTagsData.size();
        if (mIsLimitTagNum) {
            tagSize = Math.min(tagSize, mMaxTagNum);
        }
        ITag tagData;
        // 放到LinkedHashMap可以在顺序不变的情况下过滤重复数据
        Map<String, ITag> mapTagsData = new LinkedHashMap<>();
        for (int i = 0; i < tagSize; i++) {
            tagData = listTagsData.get(i);
            mapTagsData.put(tagData.getName(), tagData);
        }
        // 把标签添加到布局中
        for (String tagName : mapTagsData.keySet()) {
            tagData = mapTagsData.get(tagName);
            addTagData(tagData, false);
        }
    }

    public void setMaxTagNum(int maxTagNum) {
        mMaxTagNum = maxTagNum;
    }

    public void setIsLimitTagNum(boolean isLimitTagNum) {
        mIsLimitTagNum = isLimitTagNum;
    }

    /**
     * 获取标签列表字符串, 标签用逗号隔开.
     *
     * @return 标签列表字符串
     */
    public String getTags() {
        if (mState == TagView.INPUT) {
            int childCount = getChildCount();
            EditText editText = (EditText) getChildAt(childCount - 1);
            String text = editText.getText().toString().trim();
            if (!TextUtils.isEmpty(text)) {
                Tag tag = new Tag(text);
                tag.setId(String.valueOf(System.currentTimeMillis()));
                addTagData(tag, true);
                mMapTagsData.put(text, tag);
            }
            StringBuilder sbTag = new StringBuilder();
            for (String tagName : mMapTagsData.keySet()) {
                sbTag.append(tagName);
                sbTag.append(PERIOD_EN);
            }
            String tags = sbTag.toString();
            if (tags.length() > 0 && tags.lastIndexOf(PERIOD_EN) == tags.length() - 1) {
                tags = tags.substring(0, tags.length() - 1);
            }
            return tags;
        }
        return "";
    }

    /**
     * 生成一个新的EditText
     */
    private EditText genEditText() {
        final EditText editText = new EditText(getContext());
        editText.setBackgroundColor(getResources().getColor(android.R.color.transparent));
        editText.setMinWidth(50);
        editText.setTextSize(
                TypedValue.COMPLEX_UNIT_PX, getResources().getDimensionPixelSize(R.dimen.dimen_13));
        editText.setTextColor(getResources().getColor(R.color.goods_hint));
        editText.setCursorVisible(true);
        editText.setPadding(0, getResources().getDimensionPixelOffset(R.dimen.dimen_5), 0, getResources().getDimensionPixelOffset(R.dimen.dimen_5));
        editText.setHint(R.string.input_tag_hint);
        editText.setFilters(new InputFilter[]{new InputFilter.LengthFilter(11)});
        editText.setOnKeyListener(new OnKeyListener() {
            @Override
            public boolean onKey(View v, int keyCode, KeyEvent event) {
                if (event.getAction() == KeyEvent.ACTION_DOWN) {
                    if (keyCode == KeyEvent.KEYCODE_ENTER) {
                        String text = editText.getText().toString().trim();
                        if (!TextUtils.isEmpty(text)) {
                            Tag tag = new Tag(text);
                            addTagData(tag, true);
                            editText.setText("");
                        } else if (editText.getText().length() > 0) {
                            editText.setText("");
                        }
                        return true;
                    } else if (keyCode == KeyEvent.KEYCODE_DEL) {
                        String text = editText.getText().toString();
                        int childCount = getChildCount();
                        if (text.length() == 0 && childCount > 1) {
                            TagView tagView = (TagView) getChildAt(childCount - 2);
                            removeView(tagView);
                            mMapTagsData.remove(tagView.getTagDataName());
                            return true;
                        }
                    }
                }
                return false;
            }
        });

        editText.addTextChangedListener(new TextWatcher() {
            @Override
            public void beforeTextChanged(
                    CharSequence s, int start, int count, int after) {
            }

            @Override
            public void onTextChanged(CharSequence s, int start, int before, int count) {
                int textLength = s.length();
                if (textLength > 10) {
                    editText.setText(s.subSequence(0, 10));
                    int length = editText.getText().length();
                    editText.setSelection(length);
                    char lastChar = s.toString().charAt(length - 1);
                    if (PERIOD_CN == lastChar || PERIOD_EN == lastChar) {
                        Tag tag = new Tag(editText.getText().toString().trim());
                        addTagData(tag, true);
                        editText.setText("");
                    }
                } else {
                    String text = s.toString().trim();
                    if (text.indexOf(PERIOD_CN) != -1 || text.indexOf(PERIOD_EN) != -1) {
                        String[] words = text.split(PERIOD_CN + "|" + PERIOD_EN);
                        for (String word : words) {
                            Tag tag = new Tag(word.trim());
                            addTagData(tag, true);
                        }
                        editText.setText("");
                    }
                }
            }

            @Override
            public void afterTextChanged(Editable s) {
                if (mTagListener != null) {
                    mTagListener.onTextChanged(s.toString().trim());
                }
            }
        });
        return editText;
    }

    /**
     * 删除标签.
     *
     * @param checkedTagView 被选中的标签.
     */
    private void deleteTag(TagView checkedTagView) {
        removeView(checkedTagView);
        mMapTagsData.remove(checkedTagView.getTagDataName());
    }

    private void initAttrs(Context context, AttributeSet attrs) {
        TypedArray typedArray = context.obtainStyledAttributes(attrs, R.styleable.TagFlowLayout);
        try {
            mHorizontalSpacing = typedArray.getDimensionPixelOffset(R.styleable.TagFlowLayout_horizontalSpacing, UIUtils.dp2px((int) DEFAULT_HORIZONTAL_SPACING));
            mVerticalSpacing = typedArray.getDimensionPixelOffset(R.styleable.TagFlowLayout_verticalSpacing, UIUtils.dp2px((int) DEFAULT_VERTICAL_SPACING));
        } finally {
            typedArray.recycle();
        }
    }

    private void initVariables() {
        mState = TagView.COMMON;
        mMapTagsData = new LinkedHashMap<>();
        // 修改最大标签数量
        mMaxTagNum = Integer.MAX_VALUE;
        mIsLimitTagNum = false;
    }

    public void setTagClickHelperListener(TagClickHelperListener listener) {
        mClickHelperListener = listener;
    }

    /**
     * 标签状态变化的回调接口.
     */
    public interface TagListener {

        /**
         * 点击标签时调用.
         *
         * @param tagView 被点击的标签
         */
        void onTagClick(TagView tagView);

        /**
         * EditText文本改变时调用.
         *
         * @param text 改变后的文本
         */
        void onTextChanged(String text);
    }

    /**
     * 根据返回状态来确定是否选中当前标签的Listener
     */
    public interface TagClickHelperListener {

        boolean onTagClick(TagView tagView);

    }
}
